export const HALF_PI = Math.PI / 2;
export const TWO_PI = Math.PI * 2;
export const ZERO_TOLERANCE = 0.0001;
export const FITTING_COUNT = 100;

// 是否是顺时针
export function isClockWise(pnt1, pnt2, pnt3) {
	return (
		(pnt3[1] - pnt1[1]) * (pnt2[0] - pnt1[0]) >
		(pnt2[1] - pnt1[1]) * (pnt3[0] - pnt1[0])
	);
}

// 计算两点中点
export function calcMid(pnt1, pnt2) {
	return [(pnt1[0] + pnt2[0]) / 2, (pnt1[1] + pnt2[1]) / 2];
}

// 计算两点之间的距离
export function calcDistance(pnt1, pnt2) {
	return Math.sqrt(
		Math.pow(pnt1[0] - pnt2[0], 2) + Math.pow(pnt1[1] - pnt2[1], 2),
	);
}

// 获取基本长度
export function getBaseLength(points) {
	return Math.pow(wholeDistance(points), 0.99);
}

// 计算整体距离
export function wholeDistance(points) {
	let distance = 0;
	for (let i = 0; i < points.length - 1; i++)
		distance += calcDistance(points[i], points[i + 1]);
	return distance;
}

// 计算QBSpline曲线
export function getQBSplinePoints(points) {
	if (points.length <= 2) return points;

	const n = 2;

	const bSplinePoints = [];
	const m = points.length - n - 1;
	bSplinePoints.push(points[0]);

	for (let i = 0; i <= m; i++) {
		for (let t = 0; t <= 1; t += 0.05) {
			let y = 0;
			let x = 0;
			for (let k = 0; k <= n; k++) {
				const factor = getQuadricBSplineFactor(k, t);
				x += factor * points[i + k][0];
				y += factor * points[i + k][1];
			}
			bSplinePoints.push([x, y]);
		}
	}
	bSplinePoints.push(points[points.length - 1]);
	return bSplinePoints;
}

// 计算二次贝塞尔曲线的系数
export function getQuadricBSplineFactor(k, t) {
	if (k == 0) return Math.pow(t - 1, 2) / 2;
	if (k == 1) return (-2 * Math.pow(t, 2) + 2 * t + 1) / 2;
	if (k == 2) return Math.pow(t, 2) / 2;
	return 0;
}

// 计算第三点
export function getThirdPoint(startPnt, endPnt, angle, distance, clockWise) {
	const azimuth = getAzimuth(startPnt, endPnt);
	const alpha = clockWise ? azimuth + angle : azimuth - angle;
	const dx = distance * Math.cos(alpha);
	const dy = distance * Math.sin(alpha);

	return [endPnt[0] + dx, endPnt[1] + dy];
}

// 计算对称点
export function getTempPoint4(linePnt1, linePnt2, point) {
	const midPnt = calcMid(linePnt1, linePnt2);
	const len = calcDistance(midPnt, point);
	const angle = getAngleOfThreePoints(linePnt1, midPnt, point);
	let symPnt, distance1, distance2, mid;

	if (angle < HALF_PI) {
		distance1 = len * Math.sin(angle);
		distance2 = len * Math.cos(angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, true);
	} else if (angle >= HALF_PI && angle < Math.PI) {
		distance1 = len * Math.sin(Math.PI - angle);
		distance2 = len * Math.cos(Math.PI - angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, false);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, false);
	} else if (angle >= Math.PI && angle < Math.PI * 1.5) {
		distance1 = len * Math.sin(angle - Math.PI);
		distance2 = len * Math.cos(angle - Math.PI);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, true);
	} else {
		distance1 = len * Math.sin(Math.PI * 2 - angle);
		distance2 = len * Math.cos(Math.PI * 2 - angle);
		mid = getThirdPoint(linePnt1, midPnt, HALF_PI, distance1, true);
		symPnt = getThirdPoint(midPnt, mid, HALF_PI, distance2, false);
	}

	return symPnt;
}

// 计算三点之间的角度
export function getAngleOfThreePoints(pntA, pntB, pntC) {
	const angle = getAzimuth(pntB, pntA) - getAzimuth(pntB, pntC);
	return angle < 0 ? angle + TWO_PI : angle;
}

// 计算两点之间的角度
export function getAzimuth(startPnt, endPnt) {
	let azimuth;

	const angle = Math.asin(
		Math.abs(endPnt[1] - startPnt[1]) / calcDistance(startPnt, endPnt),
	);

	if (endPnt[1] >= startPnt[1] && endPnt[0] >= startPnt[0])
		azimuth = angle + Math.PI;
	else if (endPnt[1] >= startPnt[1] && endPnt[0] < startPnt[0])
		azimuth = TWO_PI - angle;
	else if (endPnt[1] < startPnt[1] && endPnt[0] < startPnt[0]) azimuth = angle;
	else if (endPnt[1] < startPnt[1] && endPnt[0] >= startPnt[0])
		azimuth = Math.PI - angle;

	return azimuth;
}

// 贝塞尔曲线
export function getBezierPoints(points) {
	if (points.length <= 2) return points;

	const bezierPoints = [];
	const n = points.length - 1;
	for (let t = 0; t <= 1; t += 0.01) {
		let y = 0;
		let x = 0;
		for (let index = 0; index <= n; index++) {
			const factor = getBinomialFactor(n, index);
			const a = Math.pow(t, index);
			const b = Math.pow(1 - t, n - index);
			x += factor * a * b * points[index][0];
			y += factor * a * b * points[index][1];
		}
		bezierPoints.push([x, y]);
	}
	bezierPoints.push(points[n]);
	return bezierPoints;
}

// 计算二项式系数
function getBinomialFactor(n, index) {
	return getFactorial(n) / (getFactorial(index) * getFactorial(n - index));
}

// 计算阶乘
function getFactorial(n) {
	if (n <= 1) return 1;
	if (n == 2) return 2;
	if (n == 3) return 6;
	if (n == 4) return 24;
	if (n == 5) return 120;

	let result = 1;
	for (let i = 1; i <= n; i++) result *= i;
	return result;
}

export function getBisectorNormals(t, pnt1, pnt2, pnt3) {
	const normal = getNormal(pnt1, pnt2, pnt3);
	const dist = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1]);
	const uX = normal[0] / dist;
	const uY = normal[1] / dist;
	const d1 = calcDistance(pnt1, pnt2);
	const d2 = calcDistance(pnt2, pnt3);
	let dt, x, y;
	let bisectorNormalRight = [];
	let bisectorNormalLeft = [];
	if (dist > ZERO_TOLERANCE) {
		if (isClockWise(pnt1, pnt2, pnt3)) {
			dt = t * d1;
			x = pnt2[0] - dt * uY;
			y = pnt2[1] + dt * uX;
			bisectorNormalRight = [x, y];

			dt = t * d2;
			x = pnt2[0] + dt * uY;
			y = pnt2[1] - dt * uX;
			bisectorNormalLeft = [x, y];
		} else {
			dt = t * d1;
			x = pnt2[0] + dt * uY;
			y = pnt2[1] - dt * uX;
			bisectorNormalRight = [x, y];

			dt = t * d2;
			x = pnt2[0] - dt * uY;
			y = pnt2[1] + dt * uX;
			bisectorNormalLeft = [x, y];
		}
	} else {
		x = pnt2[0] + t * (pnt1[0] - pnt2[0]);
		y = pnt2[1] + t * (pnt1[1] - pnt2[1]);
		bisectorNormalRight = [x, y];
		x = pnt2[0] + t * (pnt3[0] - pnt2[0]);
		y = pnt2[1] + t * (pnt3[1] - pnt2[1]);
		bisectorNormalLeft = [x, y];
	}
	return [bisectorNormalRight, bisectorNormalLeft];
}

function getNormal(pnt1, pnt2, pnt3) {
	let dX1 = pnt1[0] - pnt2[0];
	let dY1 = pnt1[1] - pnt2[1];
	const d1 = Math.sqrt(dX1 * dX1 + dY1 * dY1);
	dX1 /= d1;
	dY1 /= d1;

	let dX2 = pnt3[0] - pnt2[0];
	let dY2 = pnt3[1] - pnt2[1];
	const d2 = Math.sqrt(dX2 * dX2 + dY2 * dY2);
	dX2 /= d2;
	dY2 /= d2;

	const uX = dX1 + dX2;
	const uY = dY1 + dY2;
	return [uX, uY];
}

export function getCubicValue(temp, startPnt, cPnt1, cPnt2, endPnt) {
	const t = Math.max(Math.min(temp, 1), 0);
	const tp = 1 - t;
	const t2 = t * t;
	const t3 = t2 * t;
	const tp2 = tp * tp;
	const tp3 = tp2 * tp;
	const x =
		tp3 * startPnt[0] +
		3 * tp2 * t * cPnt1[0] +
		3 * tp * t2 * cPnt2[0] +
		t3 * endPnt[0];
	const y =
		tp3 * startPnt[1] +
		3 * tp2 * t * cPnt1[1] +
		3 * tp * t2 * cPnt2[1] +
		t3 * endPnt[1];
	return [x, y];
}
